package process;

import hist_data.DailyPair;
import hist_data.HistoricalDataCache;
import hist_data.MinuteBar;
import iblink.adapter.IBFacade;
import iblink.adapter.IBFacadeFactory;
import iblink.core.HistoricalData;
import iblink.core.Stock;
import iblink.util.Constants.BarSize;
import iblink.util.Constants.DataType;
import iblink.util.Constants.Duration;

import java.util.Date;
import java.util.List;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import packutil.ThreadUtil;

import util.DateUtil;

import dal.IHistoricalDataAccessor;

public class EndOfDayProcess extends AProcess {

	protected IBFacade ibFacade;
	protected IHistoricalDataAccessor histDataAccess;

	// TODO: dependency inject
	protected HistoricalDataCache histDataCache;

	protected EndOfDayProcess() {
	}

	public EndOfDayProcess(DataSourceTransactionManager txManager,
			IHistoricalDataAccessor histDataAccess,
			IBFacadeFactory ibFacadeFactory) {
		this.ibFacade = ibFacadeFactory.createIBFacade();
		this.histDataAccess = histDataAccess;
		histDataCache = new HistoricalDataCache(histDataAccess);
	}

	public void doProcess(String args[]) {

		final int retryCount = 3;
		int retryWaitMillis = 100000;

		for (int i = 1; i <= retryCount + 1; i++) {
			System.out.println("Endofdayprocess attempt #" + i);

			try {
				doProcess();
				break;
			} catch (RuntimeException e) {
				System.out.println("endofday failed");
				e.printStackTrace();

				if (i == retryCount + 1)
					throw e;

				ibFacade.tryReconnect();

				ThreadUtil.sleep(retryWaitMillis);
				retryWaitMillis *= 2;
			}
		}
	}

	protected void doProcess() {

		int failCt = 0;

		//
		// Get all securities to process (excl. already processed symbols)
		//

		List<Stock> stocksToProcess = histDataAccess
				.getUnprocessedStocks(businessDate);

		List<Stock> alreadyProcessedStocks = histDataAccess
				.getProcessedStocks(businessDate);

		// TODO: log num secs to process & date

		int ct = 0;
		for (Stock stock : stocksToProcess) {
			int percentComplete = Math
					.round((100 * (((float) ct) / stocksToProcess.size())));
			System.out.println("About to process stock (" + stock.getSymbol()
					+ ")" + ++ct + " of " + stocksToProcess.size() + " ("
					+ percentComplete + "% complete)");

			try {
				processStock(stock, alreadyProcessedStocks);
				alreadyProcessedStocks.add(stock);
			} catch (Exception e) {
				e.printStackTrace();
				// TODO: better logging here
				failCt++;
				System.out.println("Failed to process stock "
						+ stock.getSymbol() + ", fail ct=" + failCt);
			}
		}

		if (failCt > 0)
			throw new RuntimeException("Could not load all stocks");
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	protected void processStock(Stock stock, List<Stock> alreadyProcessedStocks) {

		// check to see if IB knows about this stock. if not, maybe it was
		// delisted or symbol changed?
		if (ibFacade.getStock(stock.getSymbol()) == null) {
			// TODO: log error. maybe symbol changed? requires manual work or
			// potentially automate this in future
			System.out.println("Can not find symbol " + stock.getSymbol()
					+ ". Maybe it was delisted or symbol changed? Skipping..");

			// TODO: throw better exception
			throw new RuntimeException("Can not find symbol "
					+ stock.getSymbol()
					+ ". Maybe it was delisted or symbol changed?");
		}

		// business date + 1
		System.out.println("business date: " + businessDate);
		Date endDateTime = DateUtil.adjustTradeDate(businessDate, 1);

		// TODO: this can be computed early on as a member variable
		System.out.println("next business date: " + endDateTime);

		// get daily 1 minute bars
		List<HistoricalData> histDat = ibFacade.getHistoricalData(stock,
				endDateTime, Duration.ONE_DAY, BarSize.ONE_MIN,
				DataType.TRADES, true);

		System.out.println("Got " + histDat.size() + " hist data bars");

		if (histDat.isEmpty())
			return;

		// TODO: expand, save daily summary
		// TODO: remove 0 volunme/0 trade bars before storing to DB?
		histDataCache.addBars(stock, businessDate, histDat);

		createPairs(stock, alreadyProcessedStocks, businessDate);

		//
		// TODO: TRANS START
		//

		// save bars to db
		// histDataAccess.saveHistBars(stock, histDat);
		// histDataAccess.saveDailyBar(stock, hdc.getDailyBar(stock));

		// TODO: compute correlations and other things

		// TODO: mark security as processed

		//
		// TODO: TRANS END
		//
	}

	private void createPairs(Stock stock, List<Stock> alreadyProcessedStocks,
			Date businessDate) {

		for (Stock otherStock : alreadyProcessedStocks) {
			DailyPair dp = histDataCache.getDailyPair(stock, otherStock,
					businessDate);
			System.out.println(dp);
		}
	}
}